<template>
  <div
    ref="root"
    :class="carouselClasses"
    @mouseenter.stop="handleMouseEnter"
    @mouseleave.stop="handleMouseLeave">
    <div
      class="haloe-carousel-container"
      :style="{ height: height }">
      <transition
        v-if="arrowDisplay"
        name="carousel-arrow-left">
        <button
          type="button"
          v-show="(arrow === 'always' || hover) && (loop || activeIndex > 0)"
          @mouseenter="handleButtonEnter('left')"
          @mouseleave="handleButtonLeave"
          @click.stop="throttledArrowClick(activeIndex - 1)"
          class="haloe-carousel-arrow haloe-carousel-arrow-left">
          <e-icon type="chevron-left"></e-icon>
        </button>
      </transition>
      <transition
        v-if="arrowDisplay"
        name="carousel-arrow-right">
        <button
          type="button"
          v-show="(arrow === 'always' || hover) && (loop || activeIndex < items.length - 1)"
          @mouseenter="handleButtonEnter('right')"
          @mouseleave="handleButtonLeave"
          @click.stop="throttledArrowClick(activeIndex + 1)"
          class="haloe-carousel-arrow haloe-carousel-arrow-right">
          <e-icon type="chevron-right"> </e-icon>
        </button>
      </transition>
      <slot></slot>
    </div>
    <ul
      v-if="indicatorPosition !== 'none'&&!showNumber"
      :class="indicatorsClasses">
      <li
        v-for="(item, index) in items"
        :key="index"
        :class="[
          'haloe-carousel-indicator',
          'haloe-carousel-indicator-' + direction,
          { 'is-active': index === activeIndex }]"
        @mouseenter="throttledIndicatorHover(index)"
        @click.stop="handleIndicatorClick(index)">
        <button class="haloe-carousel-button">
          <span v-if="hasLabel">{{ item.label }}</span>
        </button>
      </li>
    </ul>
    <!-- 数字切换器，只允许showNumber：true，indicator：true，direction：horizontal可以使用 -->
    <div v-if="showNumber&&indicator&&direction==='horizontal'" :class="[prefixCls + '-indicators-number']">
      <span>{{ (activeIndex+1) + '/' + items.length}}</span>
    </div>
  </div>
</template>
<script setup>
import { ref,unref,shallowRef,defineExpose,computed,watch,getCurrentInstance,onMounted,onBeforeUnmount,defineEmits,provide} from 'vue';
import { throttle } from 'lodash-unified'
import { useResizeObserver } from '@vueuse/core'
import {useOrderedChildren} from './use-carousel'
import eIcon from "../icon"
const THROTTLE_TIME = 300
const prefixCls = ref('haloe-carousel');

const props=defineProps({
  initialIndex: {
    type: Number,
    default: 0
  },
  height: String,
  trigger: {
    type: String,
    default: 'hover'
  },
  autoplay: {
    type: Boolean,
    default: true
  },
  interval: {
    type: Number,
    default: 3000
  },
  indicatorPosition: String,
  indicator: {
    type: Boolean,
    default: true
  },
  arrow: {
    type: String,
    default: 'hover'
  },
  type: String,
  loop: {
    type: Boolean,
    default: true
  },
  direction: {
    type: String,
    default: 'horizontal',
    validator(val) {
      return ['horizontal', 'vertical'].indexOf(val) !== -1;
    }
  },
  showNumber:{
    type:Boolean,
    default:false
  }
})

const {
  children: items,
  addChild: addItem,
  removeChild: removeItem,
} = useOrderedChildren(
  getCurrentInstance(),
  'CarouselItem'
)

const emits=defineEmits(['on-change'])
// let items=ref([]);
let activeIndex=ref(-1);
let containerWidth=ref(0);
let timer=ref(null)
let hover= ref(false)
let root=ref(false)

const arrowDisplay=computed(()=> {
  return props.arrow !== 'never' && props.direction !== 'vertical';
})
const hasLabel=computed(()=> {
  return items.value.some(item => item.props.label.toString().length > 0);
})


const carouselClasses=computed(()=> {
  return [
    prefixCls.value,
    prefixCls.value+`-${props.direction}`,
    {
      [prefixCls.value+`-card`]:props.type==='card'
    }
  ]
})

const indicatorsClasses=computed(()=> {
  return [
    prefixCls.value+'-indicators',
    prefixCls.value+`-indicators-${props.direction}`,
    {
      [prefixCls.value+`-indicators-labels`]:!!hasLabel.value,
      [prefixCls.value+'-indicators-outside']:props.indicatorPosition === 'outside' || props.type === 'card'
    }
  ]
})


const handleMouseEnter=()=> {
  hover.value = true;
  pauseTimer();
}

const handleMouseLeave=()=> {
  hover.value = false;
  startTimer();
}

const itemInStage=(item, index)=> {
  const _items = unref(items)
  const itemCount = _items.length
  if (itemCount === 0 || !item.states.inStage) return false
  const nextItemIndex = index + 1
  const prevItemIndex = index - 1
  const lastItemIndex = itemCount - 1
  const isLastItemActive = _items[lastItemIndex].states.active
  const isFirstItemActive = _items[0].states.active
  const isNextItemActive = _items[nextItemIndex]?.states?.active
  const isPrevItemActive = _items[prevItemIndex]?.states?.active

  if ((index === lastItemIndex && isFirstItemActive) || isNextItemActive) {
    return 'left'
  } else if ((index === 0 && isLastItemActive) || isPrevItemActive) {
    return 'right'
  }
  return false
}

const handleButtonEnter=(arrow)=> {
  if (props.direction === 'vertical') return;
  items.value.forEach((item, index) => {
    if (arrow === itemInStage(item, index)) {
      item.states.hover = true;
    }
  });
}

const handleButtonLeave=()=> {
  if (props.direction === 'vertical') return;
  items.value.forEach(item => {
    item.states.hover = false;
  });
}

// const updateItems=()=> {
//   // items.value = this.$children.filter(child => child.$options.name === 'ElCarouselItem');
// }

const resetItemPosition=(oldIndex)=> {
  items.value.forEach((item, index) => {
    item.translateItem(index, activeIndex.value, oldIndex);
  });
}

const playSlides=()=> {
  if (activeIndex.value < items.value.length - 1) {
    activeIndex.value= activeIndex.value + 1;
  } else if (props.loop) {
    activeIndex.value = 0;
  }
}

const pauseTimer=()=> {
  if (timer.value) {
    clearInterval(timer.value);
    timer.value = null;
  }
}

const startTimer=()=> {
  if (props.interval <= 0 || !props.autoplay || timer.value) return;
  timer.value = setInterval(() => playSlides(), props.interval);
}

const resetTimer=()=> {
  pauseTimer();
  startTimer();
}

const setActiveItem=(index)=> {
  if (typeof index === 'string') {
    const filteredItems = items.value.filter(item => item.props.name === index);
    if (filteredItems.length > 0) {
      index = items.value.indexOf(filteredItems[0]);
    }
  }
  index = Number(index);
  if (Number.isNaN(index) || index !== Math.floor(index)) {
    console.warn('[HaloE Warn][Carousel]index must be an integer.');
    return;
  }
  let length = items.value.length;
  const oldIndex = activeIndex.value;
  if (index < 0) {
    activeIndex.value = props.loop ? length - 1 : 0;
  } else if (index >= length) {
    activeIndex.value = props.loop ? 0 : length - 1;
  } else {
    activeIndex.value = index;
  }
  if (oldIndex === activeIndex.value) {
    resetItemPosition(oldIndex);
  }
  resetTimer();
}

const prev=()=> {
  setActiveItem(activeIndex.value - 1);
}

const next=()=> {
  setActiveItem(activeIndex.value + 1);
}

const handleIndicatorClick=(index)=> {
  activeIndex.value = index;
}

const handleIndicatorHover=(index)=> {
  if (props.trigger === 'hover' && index !== activeIndex.value) {
    activeIndex.value = index;
  }
}

// const throttledArrowClick = throttle(300, true, index => {
//   setActiveItem(index);
// });
// const throttledIndicatorHover = throttle(300, index => {
//   handleIndicatorHover(index);
// });
// methods
const throttledArrowClick = throttle(
  (index) => {
    setActiveItem(index)
  },
  THROTTLE_TIME,
  { trailing: true }
)

const throttledIndicatorHover = throttle((index) => {
  handleIndicatorHover(index)
}, THROTTLE_TIME)

const resizeObserver = shallowRef()
// lifecycle
onMounted(() => {
  resizeObserver.value = useResizeObserver(root.value, () => {
    resetItemPosition()
  })
  startTimer()
})

onBeforeUnmount(() => {
  pauseTimer()
  if (root.value && resizeObserver.value) resizeObserver.value.stop()
})

watch(()=>items.value,(val)=>{
  if(val.length>0) setActiveItem(props.initialIndex);
})

watch(()=>activeIndex.value,(val,oldVal)=>{
  resetItemPosition(oldVal)
  if(oldVal>-1){
    emits('on-change',val,oldVal)
  }
})

watch(()=>props.autoplay,(autoplay)=>{
  return autoplay?startTimer():pauseTimer()
})

watch(()=>props.loop,()=>setActiveItem(activeIndex.value))

watch(()=>props.interval,()=>{
  pauseTimer();
  startTimer();
})

const isCardType = computed(() => props.type === 'card')
const isVertical = computed(() => props.direction === 'vertical')
provide('carouselContextKey', {
  root,
  isCardType,
  isVertical,
  items,
  loop: props.loop,
  addItem,
  removeItem,
  setActiveItem,
})

defineExpose({
  /** @description manually switch slide */
  setActiveItem,
  /** @description switch to the previous slide */
  prev,
  /** @description switch to the next slide */
  next,
})
</script>
